#include "scps.h"
#include "scpstp.h"
#include "scpsudp.h"
#include "rs_config.h"
#include <stdio.h>

extern udp_Socket *udp_allsocs;  /* Pointer to first UDP socket */
extern uint32_t tp_now;

#define ADD_READ(z) \
  { \
    if (!(z->read_parent)) { \
      ((tp_Socket *) z)->read_next = ((tp_Socket *) z)->thread->read_socks; \
      if (z->read_next) \
        ((tp_Socket *) z)->read_next->read_prev = (tp_Socket *)z; \
      ((tp_Socket *) z)->read_prev = (tp_Socket *)(0x0); \
      ((tp_Socket *) z)->thread->read_socks = (tp_Socket *)z; \
      z->read_parent = (tp_Socket *)&(z->thread->read_socks); \
    } \
  }

/*
 * Handler for incoming UDP packets.
 */
/* Note: fix udp_handler to use rqts structure */
void udp_Handler (scps_np_rqts * rqts, int len, tp_Header * tp)
{
  udp_Header *up;
  char ugly[8];
  udp_Socket *s;
  byte *dp;
  struct mbuff *mbuffer;
  struct mbcluster *old_write_head;
  int old_write_off, temp_len;
  char *a;

  up = (udp_Header *) ((byte *) tp);

  if ((long) tp & (sizeof (int32_t) - 1))  /* must align xport hdr */
    {
      /* Reach in and get tp header length */
      a = (char *) tp;
      /* You'd think that we could just cast tp to a (char *) in the 
       * memcpy call, but on SunOS 4.1.3 it JUST DOESN'T WORK.  On
       * FreeBSD 2.2.2, it works fine.  
       */
      memcpy (ugly, a, 8);
      up = (udp_Header *) ugly;
    }

  if (len != ntohs (up->len))
    {
      LOG ("len = %d, up->len = %d(%d)\n", len, up->len, ntohs (up->len));
      udp_DumpHeader ((in_Header *) 0, up,
          "Discarding (IP/UDP length mismatch)");
      return;
    }

  /* demux to sockets. Accept UDP segments from 
   * any address and port, i.e., 
   * we're not doing recvfrom(), just receive() 
   */

  for (s = udp_allsocs; s; s = s->next)
    if (up->dstPort == s->myport)
      break;

  if (s == NULL)
    {
      return;
    }

  dp = (byte *) up + UDP_HDR_LEN;
  len -= UDP_HDR_LEN;

  if (len <= 0)
    return;

  /* 
   * Place the data into the receive buffer's cluster chain!
   * First, put the header into an mbuff, then copy the data
   * into a cluster. 
   */
  if ((mbuffer = alloc_mbuff (MT_HEADER)))
    {

      int offset = mbuffer->m_offset;
      mbuffer->m_len = UDP_HDR_LEN;
      memcpy ((&(mbuffer->m_pktdat) + offset), tp, mbuffer->m_len);

      /* Copy the data into the receive_buffer */
      old_write_head = s->app_rbuff->write_head;
      old_write_off = s->app_rbuff->write_off;
      if ((cb_cpdatin (s->app_rbuff, (caddr_t)dp, len, 0, 0)) <= 0)
  {
    /* add counter of trashed UDP segs!!!!! */
    free_mbuff (mbuffer);
    return;
  }

      if (old_write_head == NULL)
  {
    /* We entered cb_cpdatin w/o a write_head, and one
     * was provided to us. Figure out where it was since
     * we might have done a write > SMCLBYTES. write_off
     * will always have started at 0 in this case.
     * 12/18: cb_cpdatin will NOT provide us with a 
     * write-head if the write ends on a cluster-boundary :(
     * This shows up when the writes are exactly one cluster
     * in length... It's good that Gregs found this!!!
     * We need to put one in if not...
     */

    if (!(old_write_head = s->app_rbuff->write_head))
      /* We are full... No more for us */
      old_write_head = s->app_rbuff->last;

    temp_len = len - s->app_rbuff->write_off;

    while (temp_len > 0)
      {
        old_write_head = old_write_head->c_prev;
        temp_len -= SMCLBYTES;
      }
  }

  if ((mcput (mbuffer, old_write_head, old_write_off, len, 0)) == len) {
    /* Place the new segment into the receive queue */
    enq_mbuff (mbuffer, s->receive_buff);

    /* Wake up the receiver */
    if ((!(s->read_parent)) || s->thread->status == Blocked) {
        s->read = 0;
        /*
         * Make sure the socket is on the list of readable sockets
         */
        ADD_READ ( ((tp_Socket *) s));
        if (s->thread->status == Blocked) {
          s->thread->status = Ready;
          scheduler.num_runable++;
        }
      }

    } else {
      free_mbuff (mbuffer);
    }
  }
}
